package com.guiji.text2video.fastvideo.utils;


import org.jaudiotagger.audio.AudioFile;
import org.jaudiotagger.audio.AudioHeader;
import org.jaudiotagger.audio.generic.GenericAudioHeader;
import org.jaudiotagger.audio.wav.WavFileReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.List;

/**
 * 功能描述：语音文本处理
 *
 * @Auther: zhouguangsheng
 * @Date: 2019/8/27 10:12
 */
public class VoiceProcessUtil {

    private static final Logger LOG = LoggerFactory.getLogger(VoiceProcessUtil.class);

    private int headlength1 = 0;
    private int headlength2 = 0;


    public static int getVoiceRunningTime(File file) {
        try {
            WavFileReader read = new WavFileReader();
            AudioFile audioFile = read.read(file);
            AudioHeader audioHeader = audioFile.getAudioHeader();
            float f = ((GenericAudioHeader)audioHeader).getPreciseLength();
            int duration =  (int)(f*1000);
            return duration;
        } catch (Exception e) {
            LOG.error("获取语音时长失败：", e);
        }
        return 0;
    }

    public static void voiceJoin(String filePath1, String filePath2, String targetFilePath) {
        VoiceProcessUtil voiceProcessUtil = new VoiceProcessUtil();
        try {
            // data合成
            voiceProcessUtil.addWav(filePath1, filePath2, targetFilePath);
            voiceProcessUtil.updateFileHead(filePath1, false);
            voiceProcessUtil.updateFileHead(filePath2, false);
            //头部合成
            voiceProcessUtil.updateFileHead(targetFilePath, true);
        } catch (IOException e) {
            LOG.error("语音拼接失败：", e);
        }
    }

    public static void voiceJoin(String filePath1, String filePath2) {
        VoiceProcessUtil voiceProcessUtil = new VoiceProcessUtil();
        try {
            // data合成
            voiceProcessUtil.addWav(filePath1, filePath2);
            voiceProcessUtil.updateFileHead(filePath2, false);
            //头部合成
            voiceProcessUtil.updateFileHead(filePath1, true);
        } catch (IOException e) {
            LOG.error("语音拼接失败：", e);
        }
    }

    private static void copyFileUsingFileStreams(File source, File dest)
            throws IOException {
        InputStream input = null;
        OutputStream output = null;
        try {
            input = new FileInputStream(source);
            output = new FileOutputStream(dest);
            byte[] buf = new byte[1024];
            int bytesRead;
            while ((bytesRead = input.read(buf)) > 0) {
                output.write(buf, 0, bytesRead);
            }
        } finally {
            input.close();
            output.close();
        }
    }

    public static void voiceJoin(String targetFile, List<String> files) {
        try {
            File target = new File(targetFile);
            for (int i = 0; i < files.size(); i++) {
                if (i == 0) {
                    copyFileUsingFileStreams(new File(files.get(0)),target);
                    getVoiceRunningTime(target);
//                    FileUtils.copyFile(new File(files.get(0)), new File(targetFile));
                    //VseFileUtils.write(new File(files.get(0)), new File(targetFile), true);
                    //VoiceProcessUtil voiceProcessUtil = new VoiceProcessUtil();
                    //voiceProcessUtil.updateFileHead(targetFile, false);
                } else {
//                    voiceJoin(targetFile, files.get(i));
                }
            }
        } catch (Exception e) {
            LOG.error("语音拼接失败：", e);
        }
    }

    /*
     * 16进制字节数组转换成int
     */
    public static int byteArrayToInt(byte[] b) {
        return b[3] & 0xFF | (b[2] & 0xFF) << 8 | (b[1] & 0xFF) << 16 | (b[0] & 0xFF) << 24;
    }

    /**
     * int转换成16进制字节数组
     */
    public static byte[] intToByteArray(int a) {
        return new byte[]{(byte) ((a >> 24) & 0xFF), (byte) ((a >> 16) & 0xFF), (byte) ((a >> 8) & 0xFF),
                (byte) (a & 0xFF)};
    }

    /**
     * 字节数组的转换 0x00 00 27 56在内存中的位置是56 27 00 00 所以要反过来
     */
    public static byte[] byteToByte(byte[] a) {
        if (a.length == 4) {
            byte[] b = new byte[4];
            b[0] = a[3];
            b[1] = a[2];
            b[2] = a[1];
            b[3] = a[0];
            return b;
        }
        return null;
    }

    /**
     * 将两个wav合成一个新的wav
     */
    public void addWav(String filepath1, String filepath2, String filepath3) throws IOException {
        File file1 = new File(filepath1);
        File file2 = new File(filepath2);
        File file3 = new File(filepath3);
        if (!file3.exists()) {
            file3.createNewFile();
        }
        InputStream is1 = new FileInputStream(file1);
        InputStream is2 = new FileInputStream(file2);
        // 追加
        OutputStream os = new FileOutputStream(file3);
        byte[] tempbuffer = new byte[1024];
        int nRed = 0;
        // 将1.wav全部内容复制到3.wav
        while ((nRed = is1.read(tempbuffer)) != -1) {
            os.write(tempbuffer, 0, nRed);
            os.flush();
        }

        is1.close();
        // 跳过2.wav的文件头
        is2.skip(44);
        // 将2.wav全部内容追加复制到3.wav结尾处
        while ((nRed = is2.read(tempbuffer)) != -1) {
            os.write(tempbuffer, 0, nRed);
            os.flush();
        }
        is2.close();
        os.close();
    }

    /**
     * 将两个wav合成一个新的wav
     */
    public void addWav(String filepath1, String filepath2) throws IOException {
        File file1 = new File(filepath1);
        File file2 = new File(filepath2);

        InputStream is2 = new FileInputStream(file2);
        // 追加
        OutputStream os = new FileOutputStream(file1,true);
        byte[] tempbuffer = new byte[1024];
        int nRed = 0;

        // 跳过2.wav的文件头
        is2.skip(44);
        // 将2.wav全部内容追加复制到3.wav结尾处
        while ((nRed = is2.read(tempbuffer)) != -1) {
            os.write(tempbuffer, 0, nRed);
            os.flush();
        }
        is2.close();
        os.close();
    }

    /**
     * 更改addWav.wav.wav的文件头
     */
    public void updateFileHead(String filepath3, boolean ifUpdate) throws IOException {
        RandomAccessFile raf = new RandomAccessFile(filepath3, "rw");
        long filelength = raf.length();
        // 打开一个文件通道
        FileChannel channel = raf.getChannel();
        // 映射文件中的某一部分数据以读写模式到内存中
        // 文件头长度
        MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 44);
        byte[] b = new byte[4];
        b[0] = buffer.get(4);
        b[1] = buffer.get(5);
        b[2] = buffer.get(6);
        b[3] = buffer.get(7);
        int length1 = VoiceProcessUtil.byteArrayToInt(VoiceProcessUtil.byteToByte(b));
        // 显示data+44-8

        byte[] b1 = new byte[4];
        b1[0] = buffer.get(40);
        b1[1] = buffer.get(41);
        b1[2] = buffer.get(42);
        b1[3] = buffer.get(43);
        int length2 = VoiceProcessUtil.byteArrayToInt(VoiceProcessUtil.byteToByte(b1));

        this.headlength1 = this.headlength1 + length1;
        this.headlength2 = this.headlength2 + length2;
        // 修改头部文件
        if (ifUpdate) {
            byte[] head1 = VoiceProcessUtil.byteToByte(VoiceProcessUtil.intToByteArray(headlength1));
            byte[] head2 = VoiceProcessUtil.byteToByte(VoiceProcessUtil.intToByteArray(headlength2));
            // 进行修改操作
            buffer.put(4, head1[0]);
            buffer.put(5, head1[1]);
            buffer.put(6, head1[2]);
            buffer.put(7, head1[3]);
            buffer.put(40, head2[0]);
            buffer.put(41, head2[1]);
            buffer.put(42, head2[2]);
            buffer.put(43, head2[3]);

            // 强制输出，在buffer中的改动生效到文件
            buffer.force();
            LOG.info("文件头修改成功");

            b = new byte[4];
            b[0] = buffer.get(4);
            b[1] = buffer.get(5);
            b[2] = buffer.get(6);
            b[3] = buffer.get(7);
            length1 = VoiceProcessUtil.byteArrayToInt(VoiceProcessUtil.byteToByte(b));
            // 显示data+44-8
            b1 = new byte[4];

            b1[0] = buffer.get(40);
            b1[1] = buffer.get(41);
            b1[2] = buffer.get(42);
            b1[3] = buffer.get(43);
            length2 = VoiceProcessUtil.byteArrayToInt(VoiceProcessUtil.byteToByte(b1));

        }

        buffer.clear();
        channel.close();
        raf.close();
    }
}